package de.breitbeil.jbooking.ui.dataview;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;

import org.springframework.richclient.table.support.AbstractObjectTable;
import org.springframework.richclient.table.support.GlazedTableModel;

import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.SortedList;
import ca.odell.glazedlists.gui.AbstractTableComparatorChooser;
import ca.odell.glazedlists.gui.TableFormat;
import ca.odell.glazedlists.swing.TableComparatorChooser;

import de.breitbeil.jbooking.ui.dataview.columns.DataViewColumn;

/**
 * This is the default ObjectTable for a {@link DataView}. 
 * <p>
 * Usually there is no need to use this class directly. Just
 * add your DataViewColumns in your implementation of {@link DataView}.
 * It will be initialized and configured when the View is opened for the
 * first time. See {@link DataView} for usage and more information.
 * 
 * @author Dominik Breitbeil
 *
 * @param <T>
 */
public class DataViewTable<T> extends AbstractObjectTable {

    private List<DataViewColumn> columns;
    private Object[] initialData;
    
    /**
     * Constructs a table for the provided columns.
     * 
     * @param id view id.
     * @param columns columns of this table.
     */
    public DataViewTable(String id, List<DataViewColumn> columns) {
        super(id, extractColumnPropertyNames(columns));
        
        this.columns = columns;
    }
    
    @Override
    protected Object[] getDefaultInitialData() {
        return initialData;
    }
    
    /**
     * Sets the default initital data of the table.
     * 
     * @param initialData initital data of the table.
     */
    public void setDefaultInitialData(Object[] initialData) {
        this.initialData = initialData;
    }
    
    /**
     * Configures the JTable. 
     * Will set preferred width and {@link TableCellRenderer}s of a column. 
     * 
     * @see org.springframework.richclient.table.support.AbstractObjectTable#configureTable(javax.swing.JTable)
     */
    protected void configureTable(JTable table) {
        TableColumnModel tcm = table.getColumnModel();
        
        // Set column sizes, add renderers and comparators.
        for(int i = 0; i < columns.size(); i++) {
            DataViewColumn column = columns.get(i);
            
            // Set preferred width
            int preferredWidth = column.getPreferredWidth();
            if(preferredWidth > 0) {
                tcm.getColumn(i).setPreferredWidth(preferredWidth);
            }
            
            // Set table renderer
            TableCellRenderer renderer = column.getCellRenderer();
            if(renderer != null) {
                tcm.getColumn(i).setCellRenderer(renderer);
            }
        }
        
        super.configureTable(table);
    }
    
    /**
     * Get the default sort column. This is the first defined {@link Comparator} of the {@link DataViewColumn}s.
     */
    protected int getInitialSortColumn() {
        for(int i = 0; i < columns.size(); i++) {
            if(columns.get(i).getComparator() != null) {
                return i;
            }
        }
        throw new IllegalStateException("At least one DataViewColumn must have a comparator.");
    }
    
    
    /**
     * Get the array of selected objects in the table.
     * @return array of objects, zero length if nothing is selected
     */
    public List<T> getSelectedObjects() {
        int[] selected = getTable().getSelectedRows();
        List<T> objects = new ArrayList<T>();
        for (int i = 0; i < selected.length; i++) {
            T object = (T) getTableModel().getElementAt(selected[i]);
            objects.add(object);
        }
        return objects;
    }

    /**
     * Get the selected object in the table. If there are more than one objects selected
     * it will return the first object of the selection.
     * 
     * @return The first object of the selection, null if nothing is selected.
     */
    public T getSelectedObject() {
        List<T> list = getSelectedObjects();
        return (list.size() > 0)? getSelectedObjects().get(0) : null;
    }
    
    protected AbstractTableComparatorChooser<T> createTableSorter(JTable table, SortedList sortedList) {
        return new TableComparatorChooser<T>(table, sortedList, isMultipleColumnSort());
    }
    
    @Override
    protected GlazedTableModel createTableModel(EventList eventList) {
        return new GlazedTableModel(eventList, getColumnPropertyNames(), getModelId()) {
            protected TableFormat<T> createTableFormat() {
                return new DefaultAdvancedTableFormat() {
                    public Object getColumnValue(Object row, int column) {
                        if(columns.get(column).isObjectCellRenderer()) {
                            return row;
                        }
                        else {
                            return super.getColumnValue(row, column);
                        }
                    }
                    
                    public Comparator getColumnComparator(int column) {
                       	return columns.get(column).getComparator();
                    }
                };
            }
        };
    }

    /**
     * Helper method to get the names of a {@link DataViewColumn} list.
     */
    public static String[] extractColumnPropertyNames(List<DataViewColumn> columns) {
        String[] columnPropertyNames = new String[columns.size()];
        
        for(int i = 0; i < columns.size(); i++) {
            DataViewColumn column = columns.get(i);
            columnPropertyNames[i] = column.getColumnName();
        }
        
        return columnPropertyNames;
    }
    
}
